/* 
 * Shuriken is the legal property of its developers. Please refer to the
 * COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Runtime.InteropServices;


namespace Shuriken.Core.Utils
{
	/// <summary>
	/// Special folders for shell. 
	/// </summary>
	public enum ShellFolderSpecialConstants 
	{
		ssfALTSTARTUP = 0x1d,
		ssfAPPDATA = 0x1a,
		ssfBITBUCKET = 0xa,
		ssfCOMMONALTSTARTUP = 0x1e,
		ssfCOMMONAPPDATA = 0x23,
		ssfCOMMONDESKTOPDIR = 0x19,
		ssfCOMMONFAVORITES = 0x1f,
		ssfCOMMONPROGRAMS = 0x17,
		ssfCOMMONSTARTMENU = 0x16,
		ssfCOMMONSTARTUP = 0x18,
		ssfCONTROLS = 0x3,
		ssfCOOKIES = 0x21,
		ssfDESKTOP = 0x0,
		ssfDESKTOPDIRECTORY = 0x10,
		ssfDRIVES = 0x11,
		ssfFAVORITES = 0x6,
		ssfFONTS = 0x14,
		ssfHISTORY = 0x22,
		ssfINTERNETCACHE = 0x20,
		ssfLOCALAPPDATA = 0x1c,
		ssfMYPICTURES = 0x27,
		ssfNETHOOD = 0x13,
		ssfNETWORK = 0x12,
		ssfPERSONAL = 0x5,
		ssfPRINTERS = 0x4,
		ssfPRINTHOOD = 0x1b,
		ssfPROFILE = 0x28,
		ssfPROGRAMFILES = 0x26,
		ssfPROGRAMS = 0x2,
		ssfRECENT = 0x8,
		ssfSENDTO = 0x9,
		ssfSTARTMENU = 0xb,
		ssfSTARTUP = 0x7,
		ssfSYSTEM = 0x25,
		ssfTEMPLATES = 0x15,
		ssfWINDOWS = 0x24
	};

	/// <summary>
	/// Summary description for ShellUtils.
	/// </summary>
	public class ShellUtils
	{
		private static Shell32.Shell shell;

        /// <summary>
        /// Start menu folder for the current user 
        /// </summary>
        internal static string folderStartMenu;

        /// <summary>
        /// Start menu folder common for all the users
        /// </summary>
		internal static string folderCommonStartMenu;

        /// <summary>
        /// Favorites folder for the current user 
        /// </summary>
        internal static string folderFavorites;

        /// <summary>
        /// Favorites folder common for all the users
        /// </summary>
        internal static string folderCommonFavorites;

        /// <summary>
        /// System folder. 
        /// </summary>
        private static string folderSystem; 

		private static Shell32.Folder desktopFolder;
		
		static ShellUtils()
		{			
			shell = new Shell32.Shell();
			desktopFolder = shell.NameSpace(0);

			folderStartMenu = GetSpecialFolder(Utils.ShellFolderSpecialConstants.ssfSTARTMENU);
			folderCommonStartMenu = GetSpecialFolder(Utils.ShellFolderSpecialConstants.ssfCOMMONSTARTMENU);
            folderSystem = GetSpecialFolder(Utils.ShellFolderSpecialConstants.ssfSYSTEM);
            folderFavorites = GetSpecialFolder(Utils.ShellFolderSpecialConstants.ssfFAVORITES);
            folderCommonFavorites = GetSpecialFolder(Utils.ShellFolderSpecialConstants.ssfCOMMONFAVORITES);
		}

		[DllImport("shfolder.dll", CharSet=CharSet.Auto)]
		static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);

        public static string GetSpecialFolder(Shuriken.Core.Utils.ShellFolderSpecialConstants folder)
		{
			StringBuilder builder1 = new StringBuilder(260);
			SHGetFolderPath(IntPtr.Zero, (int) folder, IntPtr.Zero, 0, builder1);

			string specialFolder = builder1.ToString();
			return specialFolder;
		}
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="vDir"></param>
        /// <returns></returns>
        public static Shell32.Folder ShellNamespace(object vDir)
        {
            return shell.NameSpace(vDir);
        }

        public static Shell32.Shell Shell()
        {
            return shell;
        }

        /// <summary>
        /// Loads the icon associated with the give file. 
        /// </summary>
        /// <param name="path">path of the file</param>
        /// <returns>Icon for the file</returns>
        public static System.Drawing.Icon LoadImage(string path)
        {
            IntPtr hImgLarge; //the handle to the system image list
            SHFILEINFO shinfo = new SHFILEINFO();

            // Small icon 
            // hImgSmall = Win32.SHGetFileInfo(fName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | Win32.SHGFI_SMALLICON);
            System.Diagnostics.Debug.Write("Loaded Image for " + path, "Shell");

            // Large 
            hImgLarge = Win32.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON);

            if (hImgLarge == IntPtr.Zero)
            {
                return null; 
            }

            System.Drawing.Icon largeIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);



            return largeIcon;
        }

        public static System.Drawing.Icon LoadImageForControlPanel(string path)
        {
            IntPtr hImgLarge; //the handle to the system image list
            SHFILEINFO shinfo = new SHFILEINFO();

            System.Diagnostics.Debug.Write("Loaded Image for " + path, "Shell");

            // Large 
            hImgLarge = Win32.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON | Win32.SHGFI_PIDL);

            if (hImgLarge == IntPtr.Zero)
            {
                return null;
            }

            System.Drawing.Icon largeIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);

            return largeIcon;
        }

        public static System.Drawing.Icon LoadImageForFile(string path)
        {

            IntPtr hImgLarge; //the handle to the system image list
            SHFILEINFO shinfo = new SHFILEINFO();

            System.Diagnostics.Debug.Write("Loaded Image for " + path, "Shell");

            // Large 
            hImgLarge = Win32.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON);
       //IntPtr ptr1 = Shell32.SHGetFileInfo(filePath, 0, ref shfileinfo1, (uint)Marshal.SizeOf(shfileinfo1), 0x100 | iconType);
            System.Drawing.Icon largeIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);

            return largeIcon;
        }

        //public static ArrayList BuildListOfShortcutFiles()
        //{
        //    string pattern = "*.lnk";
        //    ArrayList al = new ArrayList();

        //    // Walk through the users start menu 			
        //    WalkFolder(folderStartMenu, al, pattern);
        //    WalkFolder(folderCommonStartMenu, al, pattern);

        //    // This round is needed for the clickonce shortcuts
        //    string ms_appref_pattern = "*.appref-ms";
        //    WalkFolder(folderStartMenu, al, ms_appref_pattern);
        //    WalkFolder(folderCommonStartMenu, al, ms_appref_pattern);            

        //    return al;
        //}

        //public static ArrayList BuildListOfFavorites()
        //{
        //    string pattern = "*.url";
        //    ArrayList al = new ArrayList();

        //    // Walk through the users start menu 			
        //    WalkFolder(folderFavorites, al, pattern);
        //    WalkFolder(folderCommonFavorites, al, pattern);

        //    return al;
        //}

        ///// <summary>
        ///// Enumerating all the control panel Items
        ///// </summary>
        ///// <remarks>
        ///// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_adv/conpanel.asp
        ///// </remarks>
        //public static ArrayList BuildListofControlPanelItems()
        //{
        //    ArrayList al = new ArrayList();
            
        //    Shell32.FolderItems items;
        //    items = shell.NameSpace(ShellFolderSpecialConstants.ssfCONTROLS).Items();

        //    for (int i = 0; i < items.Count; i++)
        //    {
        //        Shell32.FolderItem2 item = (Shell32.FolderItem2)items.Item(i);                       
        //        al.Add(item);
        //    }
        //    return al;
        //}

        /// <summary>
        /// Launches the folder item. 
        /// </summary>
        /// <param name="fItem">FolderItem to launch</param>
		public static void LaunchFolderItem(Shell32.FolderItem fItem)
		{
            if (fItem.IsFileSystem)
            {
                System.Diagnostics.Process.Start(fItem.Path);
            }
            else
            {
                Shell32.FolderItemVerbs verbs = fItem.Verbs();

                if (verbs.Count > 0)
                {
                    verbs.Item(0).DoIt();
                }
            }
		}

        /// <summary>
        /// Returns the path to the folder item, if item is a link then it tries to resolve the link. 
        /// </summary>
        /// <param name="fItem"></param>
        /// <returns></returns>
        /// <remarks>
        /// How to create and resolve a shortcut
        /// http://www.codeproject.com/KB/shell/create_shortcut.aspx
        /// </remarks>
        public static string GetFolderItemPath(Shell32.FolderItem fItem)
        {
            #region Sample Code
            // /**********************************************************************
            //* Function......: CreateShortcut
            //* Parameters....: lpszFileName - string that specifies a valid file name
            //*          lpszDesc - string that specifies a description for a 
            //                             shortcut
            //*          lpszShortcutPath - string that specifies a path and 
            //                                     file name of a shortcut
            //* Returns.......: S_OK on success, error code on failure
            //* Description...: Creates a Shell link object (shortcut)
            //**********************************************************************/
            //HRESULT CreateShortcut(/*in*/ LPCTSTR lpszFileName, 
            //                    /*in*/ LPCTSTR lpszDesc, 
            //                    /*in*/ LPCTSTR lpszShortcutPath)
            //{
            //    HRESULT hRes = E_FAIL;
            //    DWORD dwRet = 0;
            //    CComPtr<IShellLink> ipShellLink;
            //        // buffer that receives the null-terminated string 
            //        // for the drive and path
            //    TCHAR szPath[MAX_PATH];    
            //        // buffer that receives the address of the final 
            //        //file name component in the path
            //    LPTSTR lpszFilePart;    
            //    WCHAR wszTemp[MAX_PATH];

            //    // Retrieve the full path and file name of a specified file
            //    dwRet = GetFullPathName(lpszFileName, 
            //                       sizeof(szPath) / sizeof(TCHAR), 
            //                       szPath, &lpszFilePart);
            //    if (!dwRet)                                        
            //        return hRes;

            //    // Get a pointer to the IShellLink interface
            //    hRes = CoCreateInstance(CLSID_ShellLink,
            //                            NULL, 
            //                            CLSCTX_INPROC_SERVER,
            //                            IID_IShellLink,
            //                            (void**)&ipShellLink);

            //    if (SUCCEEDED(hRes))
            //    {
            //        // Get a pointer to the IPersistFile interface
            //        CComQIPtr<IPersistFile> ipPersistFile(ipShellLink);

            //        // Set the path to the shortcut target and add the description
            //        hRes = ipShellLink->SetPath(szPath);
            //        if (FAILED(hRes))
            //            return hRes;

            //        hRes = ipShellLink->SetDescription(lpszDesc);
            //        if (FAILED(hRes))
            //            return hRes;

            //        // IPersistFile is using LPCOLESTR, so make sure 
            //                // that the string is Unicode
            //#if !defined _UNICODE
            //        MultiByteToWideChar(CP_ACP, 0, 
            //                       lpszShortcutPath, -1, wszTemp, MAX_PATH);
            //#else
            //        wcsncpy(wszTemp, lpszShortcutPath, MAX_PATH);
            //#endif

            //        // Write the shortcut to disk
            //        hRes = ipPersistFile->Save(wszTemp, TRUE);
            //    }

            //    return hRes;
            //}
            //* Function......: ResolveShortcut
            //* Parameters....: lpszShortcutPath - string that specifies a path 
            //                                     and file name of a shortcut
            //*          lpszFilePath - string that will contain a file name
            //* Returns.......: S_OK on success, error code on failure
            //* Description...: Resolves a Shell link object (shortcut)
            //*********************************************************************/
            //HRESULT ResolveShortcut(/*in*/ LPCTSTR lpszShortcutPath,
            //                        /*out*/ LPTSTR lpszFilePath)
            //{
            //    HRESULT hRes = E_FAIL;
            //    CComPtr<IShellLink> ipShellLink;
            //        // buffer that receives the null-terminated string 
            //        // for the drive and path
            //    TCHAR szPath[MAX_PATH];     
            //        // buffer that receives the null-terminated 
            //        // string for the description
            //    TCHAR szDesc[MAX_PATH]; 
            //        // structure that receives the information about the shortcut
            //    WIN32_FIND_DATA wfd;    
            //    WCHAR wszTemp[MAX_PATH];

            //    lpszFilePath[0] = '\0';

            //    // Get a pointer to the IShellLink interface
            //    hRes = CoCreateInstance(CLSID_ShellLink,
            //                            NULL, 
            //                            CLSCTX_INPROC_SERVER,
            //                            IID_IShellLink,
            //                            (void**)&ipShellLink); 

            //    if (SUCCEEDED(hRes)) 
            //    { 
            //        // Get a pointer to the IPersistFile interface
            //        CComQIPtr<IPersistFile> ipPersistFile(ipShellLink);

            //        // IPersistFile is using LPCOLESTR, 
            //                // so make sure that the string is Unicode
            //#if !defined _UNICODE
            //        MultiByteToWideChar(CP_ACP, 0, lpszShortcutPath,
            //                                       -1, wszTemp, MAX_PATH);
            //#else
            //        wcsncpy(wszTemp, lpszShortcutPath, MAX_PATH);
            //#endif

            //        // Open the shortcut file and initialize it from its contents
            //        hRes = ipPersistFile->Load(wszTemp, STGM_READ); 
            //        if (SUCCEEDED(hRes)) 
            //        {
            //            // Try to find the target of a shortcut, 
            //                        // even if it has been moved or renamed
            //            hRes = ipShellLink->Resolve(NULL, SLR_UPDATE); 
            //            if (SUCCEEDED(hRes)) 
            //            {
            //                // Get the path to the shortcut target
            //                hRes = ipShellLink->GetPath(szPath, 
            //                                     MAX_PATH, &wfd, SLGP_RAWPATH); 
            //                if (FAILED(hRes))
            //                    return hRes;

            //                // Get the description of the target
            //                hRes = ipShellLink->GetDescription(szDesc,
            //                                             MAX_PATH); 
            //                if (FAILED(hRes))
            //                    return hRes;

            //                lstrcpyn(lpszFilePath, szPath, MAX_PATH); 
            //            } 
            //        } 
            //    } 

            //    return hRes;
            //}
            #endregion

            #region Sample Code 2
            //BOOL CShortcutMap::GetHCPShortcutTarget(OUT LPWSTR ptszTargetPathSpec, IN CONST UINT cch) {
            //    LPITEMIDLIST pidl;
            //    m_psl->GetIDList(&pidl);
            //    STRRET strret;
            //    m_psf->GetDisplayNameOf(pidl,SHGDN_FORPARSING,&strret);
            //    StrRetToBuf(&strret, pidl, ptszTargetPathSpec, cch);
            //    return lstrlen(ptszTargetPathSpec)?true:false;
            //}
            #endregion

            string path = fItem.Path; 
            
            try
            {
                if (fItem.IsLink)
                {
                    Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)fItem.GetLink;
                    path = link.Path;

                }
            }
            catch (Exception excp)
            {
                Tracer.WriteVerbose("ShellLinkObject.Path failed. Message: " + excp.Message + " for Item (" +fItem.Path + ")!", "Shell");
            }

            return path; 
        }

        /// <summary>
        /// Reveal the location of the file 
        /// </summary>
        /// <param name="folderItem">Folder Item</param>
        internal static void RevealFolderItem(Shell32.FolderItem folderItem)
        {
            if (folderItem.IsLink)
            {
                string path = GetFolderItemPath(folderItem);

                System.Diagnostics.Process.Start("explorer.exe","/select," + path);
            }
        }

        /// <summary>
        /// Searches the folder recusively for files that matches the pattern provided. 
        /// </summary>
        /// <param name="folder">folder to search on</param>
        /// <param name="al">list to store the results of the search</param>
        /// <param name="searchPattern">pattern to use for searching files</param>
        private static void WalkFolder(string folder, ArrayList al, string searchPattern)
        {
            string[] files = System.IO.Directory.GetFiles(folder, searchPattern);
            string[] dirs = System.IO.Directory.GetDirectories(folder);

            foreach (string file in files)
            {
                Shell32.FolderItem linkItem = GetFolderItemFromFile(file); 
                al.Add(linkItem);
            }

            foreach (string dir in dirs)
                WalkFolder(dir, al, searchPattern);
        }

        /// <summary>
        /// Gets the ShellFolderItem for the specified file 
        /// </summary>
        /// <param name="fileName">file name</param>
        /// <returns>folder item object</returns>
        public static Shell32.FolderItem GetFolderItemFromFile(string fileName)
        {
            Shell32.FolderItem folderItem = desktopFolder.ParseName(fileName);
            return folderItem;
        }
    }





}
